module lpuart_basic_mcp_sync #(parameter DATA_WIDTH = 8)
(
  output                  aready,
  input  [DATA_WIDTH-1:0] adatain,
  input                   asend,
  input                   aclk,
  input                   arst_n,

  output [DATA_WIDTH-1:0] bdata,
  output                  bvalid,
  input                   bload,
  input                   bclk,
  input                   brst_n
);

wire [DATA_WIDTH-1:0] adata;
wire                  b_ack;
wire                  a_en;
wire                  bq2_en;
wire                  aq2_ack;

sync2 async (.q(aq2_ack), .d(b_ack), .clk(aclk), .rst_n(arst_n));
sync2 bsync (.q(bq2_en),  .d(a_en),  .clk(bclk), .rst_n(brst_n));

amcp_send #(.DATA_WIDTH(DATA_WIDTH)) alogic
(
  .adata     (adata),
  .a_en      (a_en),
  .aready    (aready),
  .adatain   (adatain),
  .asend     (asend),
  .aq2_ack   (aq2_ack),
  .aclk      (aclk),
  .arst_n    (arst_n)
);

bmcp_recv #(.DATA_WIDTH(DATA_WIDTH)) blogic
(
  .bdata   (bdata),
  .bvalid  (bvalid),
  .b_ack   (b_ack),
  .adata   (adata),
  .bload   (bload),
  .bq2_en  (bq2_en),
  .bclk    (bclk),
  .brst_n  (brst_n)
);

endmodule

module sync2(
  output logic q,
  input  logic d, clk, rst_n
);

reg q1;

always_ff @(posedge clk, negedge rst_n)
  if(!rst_n) {q, q1} <= '0;
  else       {q, q1} <= {q1, d};

endmodule

module plsgen(
  output     pulse, 
  output reg q,
  input      d,
  input      clk, rst_n
);

always_ff @(posedge clk, negedge rst_n)
  if(!rst_n) q <= '0;
  else       q <= d;

assign pulse = q ^ d;

endmodule

module asend_fsm(
  output logic aready,
  input  logic asend,
  input  logic aack,
  input  logic aclk,
  input  logic arst_n
);

enum logic {READY = '1,
            BUSY  = '0} state, next;

always_ff @(posedge aclk, negedge arst_n)
  if(!arst_n) state <= READY;
  else        state <= next;

always_comb begin
  case(state)
    READY: if(asend) next = BUSY;
           else      next = READY;
    BUSY : if(aack)  next = READY;
           else      next = BUSY;
  endcase
end

assign aready = state;

endmodule

module back_fsm(
  output bvalid,
  input  bload,
  input  b_en,
  input  bclk,
  input  brst_n
);

enum logic {READY = '1,
            WAIT  = '0} state, next;

always_ff @(posedge bclk, negedge brst_n)
  if(!brst_n) state <= WAIT;
  else       state <= next;

always_comb begin
  case(state)
    READY: if(bload) next = WAIT;
           else      next = READY;
    WAIT : if(b_en)  next = READY;
           else      next = WAIT;
  endcase
end

assign bvalid = state;

endmodule

module bmcp_recv #(parameter DATA_WIDTH = 8)
(
  output reg [DATA_WIDTH-1:0] bdata,
  output                      bvalid,
  output reg                  b_ack,
  input      [DATA_WIDTH-1:0] adata,
  input                       bload,
  input                       bq2_en,
  input                       bclk,
  input                       brst_n
);

wire b_en;

plsgen pg1(.pulse(b_en), .q(), .d(bq2_en), .clk(bclk), .rst_n(brst_n));

back_fsm fsm(
  .bvalid (bvalid),
  .bload  (bload),
  .b_en   (b_en),
  .bclk   (bclk),
  .brst_n (brst_n)
);

wire bload_data = bvalid & bload;

always_ff @(posedge bclk, negedge brst_n)
  if     (!brst_n)    b_ack <= '0;
  else if(bload_data) b_ack <= ~b_ack;

always_ff @(posedge bclk, negedge brst_n)
  if     (!brst_n)    bdata <= '0;
  else if(bload_data) bdata <= adata;

endmodule

module amcp_send #(parameter DATA_WIDTH = 8)
(
  output reg [DATA_WIDTH-1:0] adata,
  output reg                  a_en,
  output                      aready,
  input      [DATA_WIDTH-1:0] adatain,
  input                       asend,
  input                       aq2_ack,
  input                       aclk,
  input                       arst_n
);

wire aack;

plsgen pg1(.pulse(aack), .q(), .d(aq2_ack), .clk(aclk), .rst_n(arst_n));

asend_fsm fsm(
  .aready   (aready),
  .asend    (asend),
  .aack     (aack),
  .aclk     (aclk),
  .arst_n   (arst_n)
);


wire anxt_data = aready & asend;

always_ff @(posedge aclk, negedge arst_n)
  if     (!arst_n)   a_en <= '0;
  else if(anxt_data) a_en <= ~a_en;

always_ff @(posedge aclk, negedge arst_n)
  if     (!arst_n)   adata <= '0;
  else if(anxt_data) adata <= adatain;

endmodule

